﻿package com.thorqq.magictimer.download;

import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import com.thorqq.magictimer.db.DownloadDBhelper;
import com.thorqq.magictimer.util.Util;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class Downloader
{
    public static final int DL_STATE_ERROR = -1;
    public static final int DL_STATE_INIT = 1;
//    public static final int DL_STATE_CONNECTING = 2;
    public static final int DL_STATE_DOWNLOADING = 3;
    public static final int DL_STATE_PAUSE = 4;
    public static final int DL_STATE_COMPLETED = 5;
    public static final int DL_STATE_DELETE = 6;
    
    private static final int TO_NONE = 0;
    private static final int TO_START = 1;
    private static final int TO_PAUSE = 2;
    private static final int TO_DELETE = 3;

    private static final int MSG_THREAD_STATE = 1;
    
    private static final int BUFFER_SIZE = 4096;
    private static final int UPDATE_DB_SPEED = 20;
    private static final int NOTIFY_MSG_SPEED = 5;
    private static final int TEST_SPEED_INTERVAL = 1*1000;
    
    private TaskInfo mTaskInfo;
    private DownloadListener mListener;
    
    private int mThreadCount;// 线程数
    
    private List<ThreadInfo> mThreadInfos;// 存放下载信息类的集合
    private List<Integer> mThreadStates;
    private DownloadDBhelper mDBHelper;// 工具类
//    private int mState = DL_STATE_INIT;//下载状态
    private int mTodo = TO_NONE;

    private java.util.Timer mTimer;
    private java.util.TimerTask mTimerTaskNetSpeed;

    //已下载的字节数
    private int mSizeDownloaded = 0;
    private int mSizeDownloadedBak = 0;
    synchronized private void setSize(int size)
    {
        mSizeDownloaded = size;
    }

    synchronized private void addSize(int size)
    {
        mSizeDownloaded += size;
    }

    synchronized private int getSize()
    {
        return mSizeDownloaded;
    }

    synchronized private int getSizeBak()
    {
        return mSizeDownloadedBak;
    }

    synchronized private void setSizeBak(int size)
    {
        mSizeDownloadedBak = size;
    }

    private Handler mHandler = new Handler()
    {
        public void handleMessage(Message msg)
        {
            if (msg.what == MSG_THREAD_STATE)
            {
                int threadid = (Integer)msg.obj;
                int state = msg.arg1;
                                
                //更新线程状态
                Downloader.this.mThreadStates.set(threadid, state);
                
                if(mTodo == TO_DELETE)
                {
                    mTaskInfo.setState(DL_STATE_DELETE);
                    mDBHelper.deleteThreadInfo(mTaskInfo.getTaskid());
                }
                else if(Downloader.this.isFinish())
                {
                    mTaskInfo.setState(DL_STATE_COMPLETED);
                    mDBHelper.deleteThreadInfo(mTaskInfo.getTaskid());
                }
                else if(Downloader.this.isDownloading())
                {
                    mTaskInfo.setState(DL_STATE_DOWNLOADING);
                }
                else if(Downloader.this.isError())
                {
                    mTaskInfo.setState(DL_STATE_ERROR);
                }
                else if(Downloader.this.isPause())
                {
                    mTaskInfo.setState(DL_STATE_PAUSE);
                }
                
                if(mTaskInfo.getState() == DL_STATE_COMPLETED
                        || mTaskInfo.getState() == DL_STATE_PAUSE
                        || mTaskInfo.getState() == DL_STATE_ERROR
                        || mTaskInfo.getState() == DL_STATE_DELETE)
                {
                    clearToDo(TO_PAUSE);
                    clearToDo(TO_DELETE);
                }
                else if(mTaskInfo.getState() == DL_STATE_DOWNLOADING)
                {
                    clearToDo(TO_START);
                }
                
                mTaskInfo.setComplete(getSize()); 
            }
        }
    };

    public Downloader(Context context, TaskInfo taskInfo, int threadcount, DownloadDBhelper dao)
    {
        this.mTaskInfo = taskInfo;
        this.mThreadCount = threadcount;
        this.mDBHelper = dao;
    }

    public void start()
    {
        new DownloadTask().execute();
    }
    
    //定时测速/定时更新进度
    private void startSpeedTest()
    {
        mTimerTaskNetSpeed = new java.util.TimerTask() 
        {
            @Override
            public void run() 
            {
                int speed = (int)((getSize() - getSizeBak())*1000.0f/TEST_SPEED_INTERVAL);
                mTaskInfo.setSpeed(speed < 0 ? 0 : speed);
                setSizeBak(getSize());
                
                Log.d("Downloader", String.format("[" + mTaskInfo.getTaskid() + "]进度：%.2f%%(%d/%d)", 
                        mTaskInfo.getComplete()*100.0f/mTaskInfo.getFileSize(),
                        mTaskInfo.getComplete(),
                        mTaskInfo.getFileSize()));
                
                //下载停止时，更新已下载数
                if(mTaskInfo.getState() == DL_STATE_COMPLETED
                        || mTaskInfo.getState() == DL_STATE_PAUSE
                        || mTaskInfo.getState() == DL_STATE_ERROR
                        || mTaskInfo.getState() == DL_STATE_DELETE)
                {
                    cancelSpeedTest();
                    mDBHelper.updateTaskInfo(mTaskInfo.getTaskid(), mTaskInfo.getFileSize(), mTaskInfo.getComplete());
                }            
                
                Downloader.this.notifyThreadInfo();

                if(mTaskInfo.getState() == DL_STATE_COMPLETED)
                {
                    reset();
                }
            }
        };
        
        setSizeBak(getSize());
        mTimer = new java.util.Timer();
        mTimer.schedule(mTimerTaskNetSpeed, 0, TEST_SPEED_INTERVAL);
    }
    
    private void cancelSpeedTest()
    {
        mTaskInfo.setSpeed(0);
        mTimer.cancel();
        mTimer.purge();
    }
    
    // 设置暂停
    public void pause()
    {
        mTodo = TO_PAUSE;
    }
    
    public void delete()
    {
        mTodo = TO_DELETE;
    }
    
    public boolean isToPause()
    {
        return (mTodo == TO_PAUSE || mTodo == TO_DELETE) ? true : false;
    }
    
    public boolean isToStart()
    {
        return mTodo == TO_START ? true : false;
    }
    
    public void clearToDo(int todo)
    {
        if(todo == mTodo)
        {
            mTodo = TO_NONE;
        }
    }
    
    // 重置下载状态
    public void reset()
    {
        mTaskInfo.setState(DL_STATE_INIT);
    }
    
    public void setDownloadListener(DownloadListener listener)
    {
        mListener = listener;
    }

    //只要有一个是正在下载，就是正在下载
    public boolean isDownloading()
    {
        if(mThreadStates == null || mThreadStates.size() == 0)
        {
            return false;
        }
        
        //正在连接中
        if(mTodo == TO_START)
        {
            return true;
        }
        
        //正在下载
        for(Integer state : mThreadStates)
        {
            if(state == DownloadThread.TD_STATE_DOWNLOADING)
            {
                return true;
            }
        }
        return false;
    }
    
    //只要有一个是正在下载，就不是停止
    public boolean isPause()
    {
        for(Integer state : mThreadStates)
        {
            if(state == DownloadThread.TD_STATE_DOWNLOADING)
            {
                return false;
            }
        }
        return true;
    }

    //所有的都是完成，才是完成
    public boolean isFinish()
    {
        int flag = 0;
        for(Integer state : mThreadStates)
        {
            if(state == DownloadThread.TD_STATE_FINISH)
            {
                flag++;
            }
        }
        
        if(flag == mThreadStates.size())
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    //有一个有错误，其他的都不是正在运行
    public boolean isError()
    {
        int errCnt = 0;
        for(Integer state : mThreadStates)
        {
            if(state == DownloadThread.TD_STATE_DOWNLOADING)
            {
                return false;
            }
            else if(state == DownloadThread.TD_STATE_ERROR)
            {
                errCnt++;
            }
        }
        
        if(errCnt > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    private void notifyThreadInfo()
    {
        if(this.mListener != null)
        {
            Downloader.this.mListener.OnDownloadInfoUpdate(mTaskInfo.getTaskid());
        }
    }
    
    private boolean initFileAndThread()
    {
        try
        {
            boolean bNeedUpdateTaskInfoInDB = false;

            //初始化本地保存文件
            boolean fileChanged = false;
            File file = new File(mTaskInfo.getLocalFile());
            if (!file.exists())
            {
                file.createNewFile();
            }
//            else
//            {
//                for(int i = 1; i < 100000; i++)
//                {
//                    String path = file.getCanonicalPath().substring(0, file.getCanonicalPath().lastIndexOf("/")+1);
//                    File newfile = new File(path+ "("+ i +")" + file.getName());
//                    if (!newfile.exists())
//                    {
//                        file = newfile;
//                        file.createNewFile();
//                        bNeedUpdateTaskInfoInDB = true;
//                        break;
//                    }
//                }
//            }
//            mTaskInfo.setLocalFile(file.getAbsolutePath());
//            Downloader.this.notifyThreadInfo();

            //获取本地保存的各个线程的下载信息
            mThreadInfos = mDBHelper.getThreadInfo(mTaskInfo.getTaskid());

            //连接网络，初始化文件大小
            //TODO 这里最好判断一下对端是否支持多线程下载
            int fileSize = 0;
            URL url = new URL(mTaskInfo.getUrlstring());
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(5000);
            connection.setRequestMethod("GET");
            connection.connect();
            fileSize = connection.getContentLength();
            connection.disconnect();
            
            if(fileSize <= 0)
            {
                Util.log("Get file size from net failed");
                return false;
            }
            
            //如果文件大小发生改变
            if(mTaskInfo.getFileSize() != fileSize
                    || mTaskInfo.getComplete() > mTaskInfo.getFileSize())
            {
                mTaskInfo.setFileSize(fileSize);
                mTaskInfo.setComplete(0);
                bNeedUpdateTaskInfoInDB = true;
            }
                        
            RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
            if(accessFile.length() != mTaskInfo.getFileSize() )
            {
                Util.log("重新分配文件大小: from " + accessFile.length() + 
                        " to " + mTaskInfo.getFileSize());
                accessFile.setLength(mTaskInfo.getFileSize());
                fileChanged = true;
            }
            accessFile.close();
            
            //重新生成文件区段信息
            if(fileChanged
                    || mThreadInfos == null
                    || mThreadInfos.size() == 0)
            {
                if(mThreadInfos != null)
                {
                    mThreadInfos.clear();
                }
                
                //按照线程数划分成多个文件段
                mTaskInfo.setComplete(0);
                bNeedUpdateTaskInfoInDB = true;
                int range = mTaskInfo.getFileSize() / mThreadCount;
                mThreadInfos = new ArrayList<ThreadInfo>();
                for (int i = 0; i < mThreadCount - 1; i++)
                {
                    ThreadInfo info = new ThreadInfo(mTaskInfo.getTaskid(), i, i * range, (i + 1) * range - 1, 0);
                    mThreadInfos.add(info);
                }
                
                ThreadInfo info = new ThreadInfo(mTaskInfo.getTaskid(), 
                        mThreadCount - 1, (mThreadCount - 1) * range, mTaskInfo.getFileSize() - 1, 0);
                mThreadInfos.add(info);
                mDBHelper.saveThreadInfo(mThreadInfos);
                this.setSize(0);
            }
            
            if(bNeedUpdateTaskInfoInDB)
            {
                mDBHelper.updateTaskInfo(mTaskInfo.getTaskid(), fileSize, mTaskInfo.getComplete());
            }
            this.setSize(mTaskInfo.getComplete());
            
        } catch (Exception e)
        {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private void doDownload()
    {
        if (mThreadInfos != null)
        {
            if (mTaskInfo.getState() == DL_STATE_DOWNLOADING)
            {
                return;
            }
            
            mTaskInfo.setState(DL_STATE_DOWNLOADING);
            int threadNum = 0;
            mThreadStates = new ArrayList<Integer>();
            for (ThreadInfo info : mThreadInfos)
            {
                if(info.getStartPos() + info.getCompeleteSize() < info.getEndPos() + 1)
                {
                    threadNum++;
                    mThreadStates.add(DownloadThread.TD_STATE_DOWNLOADING);
                    new DownloadThread(info).start();
                }
                else
                {
                    Util.log("[id:" + info.getThreadId() + "]: " + "已下载完成，无需再下载");

                    mThreadStates.add(DownloadThread.TD_STATE_FINISH);
                }
            }
            
            //特殊情况：从数据库里读出的下载信息表示全都已经下载完成了
            if(threadNum == 0)
            {
                Util.log( "全部下载完成");
                mDBHelper.deleteThreadInfo(mTaskInfo.getTaskid());
                mTaskInfo.setState(DL_STATE_COMPLETED);
                mTaskInfo.setComplete(getSize());
                
                Downloader.this.notifyThreadInfo();
            }

        }
    }
    
    synchronized private void updataInfos(int taskid, int threadId, int compeleteSize)
    {
        mDBHelper.updataInfos(taskid, threadId, compeleteSize);
    }
    
    private class DownloadTask extends AsyncTask<Void, Boolean, Boolean> 
    {
        
        @Override
        protected Boolean doInBackground(Void... params) 
        {
            mTodo = TO_START;
//            mTaskInfo.setState(DL_STATE_CONNECTING);
            Downloader.this.notifyThreadInfo();
            return initFileAndThread();
        }
        
        @Override
        protected void onProgressUpdate(Boolean... b )
        {
        }

        @Override
        protected void onPostExecute(Boolean result) 
        {
            mTaskInfo.setComplete(getSize());
            
            if(result)
            {
                Downloader.this.notifyThreadInfo();
                
                startSpeedTest();
                Downloader.this.doDownload();
            }
            else
            {
                mTaskInfo.setState(DL_STATE_ERROR);
                mTaskInfo.setSpeed(0);
                Downloader.this.notifyThreadInfo();
            }
        }
    }


    //多线程下载
    //发送的消息类型：
    //正在下载 DL_STATE_DOWNLOADING  
    //下载停止 DL_STATE_PAUSE
    //发生异常 DL_STATE_ERROR
    //下载结束 DL_STATE_FINISH
    private class DownloadThread extends Thread
    {
        public static final int TD_STATE_ERROR = -11;
        public static final int TD_STATE_DOWNLOADING = 11;
        public static final int TD_STATE_PAUSE = 12;
        public static final int TD_STATE_FINISH = 13;
        
        private ThreadInfo mInfo;

        public DownloadThread(ThreadInfo info)
        {
            mInfo = info;
        }
        
        private void sendMessage(int state)
        {            
            Message message = Message.obtain();
            message.what = MSG_THREAD_STATE;
            message.obj = mInfo.getThreadId();
            message.arg1 = state;
            mHandler.sendMessage(message);
        }

        @Override
        public void run()
        {
            // 设置范围，格式为Range：bytes x-y;
            Util.log("[id:" + this.mInfo.getThreadId() + "]: " + 
                    "Range bytes: " + (mInfo.getStartPos()+mInfo.getCompeleteSize()) + 
                    "(" + mInfo.getStartPos() +"+"+ mInfo.getCompeleteSize() + ")->" + mInfo.getEndPos());

            HttpURLConnection connection = null;
            RandomAccessFile randomAccessFile = null;
            InputStream is = null;
            try
            {
                URL url = new URL(Downloader.this.mTaskInfo.getUrlstring());
                connection = (HttpURLConnection) url.openConnection();
                connection.setConnectTimeout(5000);
                connection.setRequestMethod("GET");
                connection.setRequestProperty("Range", "bytes=" + 
                        (mInfo.getStartPos() + mInfo.getCompeleteSize()) + "-" + mInfo.getEndPos());
                connection.connect();
                
                randomAccessFile = new RandomAccessFile(mTaskInfo.getLocalFile(), "rwd");
                randomAccessFile.seek(mInfo.getStartPos() + mInfo.getCompeleteSize());

                is = connection.getInputStream();
                byte[] buffer = new byte[BUFFER_SIZE];
                int length = -1;
                int updateFlag = 0;
                int notifyFlag = 0;
                while ((length = is.read(buffer)) != -1)
                {
                    randomAccessFile.write(buffer, 0, length);
                    mInfo.setCompeleteSize(mInfo.getCompeleteSize() + length);
                    // 更新数据库中的本线程的下载信息
                    if(++updateFlag >= UPDATE_DB_SPEED)
                    {
                        Downloader.this.updataInfos(Downloader.this.mTaskInfo.getTaskid(), 
                                mInfo.getThreadId(), mInfo.getCompeleteSize());
                        updateFlag = 0;
                    }
                    
                    // 用消息将下载信息传给进度条，对进度条进行更新
                    Downloader.this.addSize(length);      
                    if(++notifyFlag >= NOTIFY_MSG_SPEED)
                    {
                        this.sendMessage(TD_STATE_DOWNLOADING);
                        notifyFlag = 0;
                    }
                    
                    // 判断是否要暂停
                    if (Downloader.this.isToPause())
                    {
                        this.sendMessage(TD_STATE_PAUSE);
                        Util.log("[id:" + mInfo.getThreadId() + "]: " + "下载暂停1");
                        break;
                    }                    
                }
            } catch (Exception e)
            {
                this.sendMessage(TD_STATE_ERROR);
                e.printStackTrace();
            } finally
            {                
                Downloader.this.updataInfos(Downloader.this.mTaskInfo.getTaskid(),
                        mInfo.getThreadId(), mInfo.getCompeleteSize());
                
                try
                {
                    is.close();
                    randomAccessFile.close();
                    connection.disconnect();
                } catch (Exception e)
                {
                    this.sendMessage(TD_STATE_ERROR);
                    e.printStackTrace();
                }
                
                if(mInfo.getStartPos() + mInfo.getCompeleteSize() >= mInfo.getEndPos() + 1)
                {
                    this.sendMessage(TD_STATE_FINISH);
                }
            }

        }
    }
    

    
}
